﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Numerics;
using ArgusLib.Drawing;
using SBitmap = System.Drawing.Bitmap;
using Color = System.Drawing.Color;

namespace ArgusLib.Fractals.OrbitFractals
{
	public class RendererEscapeTime : IRendererSimplyConnectedSet
	{
		private delegate Color ColorProvider(int iterations);

		private RenderParameterEscapeTime parameter;
		public RenderParameterEscapeTime Parameter
		{
			get { return this.parameter; }
			set
			{
				if (value == null)
					throw new ArgumentNullException();
				this.parameter = value;
			}
		}
		IRenderParameter IRenderer.Parameter { get { return this.Parameter; } set { this.Parameter = (RenderParameterEscapeTime)value; } }
		IRenderParameterSimplyConnectedSet IRendererSimplyConnectedSet.Parameter { get { return this.Parameter; } set { this.Parameter = (RenderParameterEscapeTime)value; } }

		public RendererEscapeTime(RenderParameterEscapeTime parameter)
		{
			this.Parameter = parameter;
		}

		public bool CanRender(IDataSet dataSet)
		{
			return (dataSet is DataSet<Complex>);
		}

		SBitmap IRenderer.Render(IDataSet dataSet)
		{
			return this.Render(dataSet as DataSet<Complex>);
		}

		public SBitmap Render(DataSet<Complex> dataSet)
		{
			if (dataSet == null)
				throw new ArgumentNullException("dataSet");

			ColorProvider colorProvider = this.GetColorProvider();

			using (ExtendedBitmap bitmap = new ExtendedBitmap(dataSet.DimensionX, dataSet.DimensionY))
			{
				bitmap.Lock();

				for (int x = 0; x < bitmap.Width; x++)
				{
					for (int y = 0; y < bitmap.Height; y++)
					{
						if (dataSet.IsNull(x, y) == true || dataSet.Iterations[x,y] >= dataSet.CalculationParameter.Iterations)
						{
							bitmap.SetPixel(x, y, this.Parameter.SetColor);
						}
						else
						{
							Color c = colorProvider(dataSet.Iterations[x, y]);
							bitmap.SetPixel(x, y, c);
						}
					}
				}

				bitmap.Unlock();
				return bitmap.GetBitmap();
			}
		}

		private ColorProvider GetColorProvider()
		{
			if (this.Parameter.Coloring == RenderParameterEscapeTime.Colorings.RedYellow)
			{
				return new ColorProvider((iterations) =>
					{
						byte[] b = GetColorBytes_2(iterations);
						return Color.FromArgb(255, b[0], b[1], 0);
					});
			}

			return null;
		}

		private static byte[] GetColorBytes_2(Int32 i)
		{
			byte a = 0;
			byte b = 0;
			if (i < 16)
				a = (byte)((i) * 16 + 15);
			else
			{
				if (i < 11)
					a = (byte)(i * 25);
				else
				{
					if (i > 4 * 16)
						i = i % (4 * 16);

					if (i < 16)
						a = (byte)((i + 1) * 16 - 1);
					else if (i < 2 * 16)
					{
						a = 255;
						i = i - 16;
						b = (byte)((i + 1) * 16 - 1);
					}
					else if (i < 3 * 16)
					{
						a = 255;
						i = i - 2 * 16;
						b = (byte)(255 - ((i + 1) * 16 - 1));
					}
					else if (i < 4 * 16)
					{
						i = i - 3 * 16;
						a = (byte)(255 - ((i + 1) * 16 - 1));
					}
				}
			}
			return new byte[] { a, b };
		}

		private static byte[] GetColorBytes_3(Int32 i)
		{
			byte a, b, c;
			a = b = c = 0;

			if (i < 11)
				a = (byte)(i * 25);
			else
			{
				if (i > 8 * 16)
					i = i % (8 * 16);

				if (i < 16)
					a = (byte)((i + 1) * 16 - 1);
				else if (i < 2 * 16)
				{
					a = 255;
					i = i - 16;
					b = (byte)((i + 1) * 16 - 1);
				}
				else if (i < 3 * 16)
				{
					a = 255;
					i = i - 2 * 16;
					b = (byte)(255 - ((i + 1) * 16 - 1));
				}
				else if (i < 4 * 16)
				{
					i = i - 3 * 16;
					a = (byte)(255 - ((i + 1) * 16 - 1));
				}
				else if (i < 5 * 16)
				{
					i = i - 4 * 16;
					b = (byte)((i + 1) * 16 - 1);
				}
				else if (i < 6 * 16)
				{
					b = 255;
					i = i - 5 * 16;
					c = (byte)((i + 1) * 16 - 1);
				}
				else if (i < 7 * 16)
				{
					b = 255;
					i = i - 6 * 16;
					c = (byte)(255 - ((i + 1) * 16 - 1));
				}
				else if (i < 8 * 16)
				{
					i = i - 7 * 16;
					b = (byte)(255 - ((i + 1) * 16 - 1));
				}
			}
			return new byte[] { a, b, c };
		}
	}
}
